﻿package com.flashframeless.draw {

	/**
	 * Flexible Line Class - Created by FlashFrameLess Team NOV/2009
	 *
	 * @author	FlashFrameLess	- http://www.flashframeless.com/
	 * @author	FabioTNT 		- http://www.fabiotnt.com/
	 */
		
	import flash.events.Event;
	import flash.display.Sprite;

	public class FlexibleLine extends Sprite {
	
		 /*------------------------------------------------------------------------------------------------*\
		|* Class Variables --------------------------------------------------------------------------------*|
		\*------------------------------------------------------------------------------------------------*/
		
		//Privates
		private var _startPoint	:Object;
		private var _endPoint	:Object
		private var _segments	:Number;
		
		//Publics
		public var gravity		:Number;
	    public var elasticity	:Number;
	    public var friction		:Number;
		public var lineHeight	:Number = 1;
		public var lineColor	:Number = 0x000000;
	
		 /*------------------------------------------------------------------------------------------------*\
		|* CONSTRUCTOR ------------------------------------------------------------------------------------*|
		\*------------------------------------------------------------------------------------------------*/
	
		public function FlexibleLine( startPoint:Object, endPoint:Object, segments:uint = 10, __gravity:Number = 10, __elasticity:Number = .2, __friction:Number = .9 ){
		
			//Invoke super class.
			super();
			
			_startPoint = startPoint;
			_endPoint	= endPoint;
			_segments	= segments;
			
			gravity		= __gravity;
			elasticity	= __elasticity;
			friction	= __friction;
			
			addEventListener( Event.ADDED_TO_STAGE, addToStageHandler, false, 0, true );
			
		}
		
		 /*------------------------------------------------------------------------------------------------*\
		|* Class Methods ----------------------------------------------------------------------------------*|
		\*------------------------------------------------------------------------------------------------*/
		
		/**
		 *  Instantiate.
		 *	Fill varibles with respective Object.
		 *	
		 * @return void
		 */
		private function instantiate():void {
			
			//
			setupInteractions();
			
		}
		
		/**
		 *  Setup Interactions.
		 *	Add Listeners, childs on stage, handlers and others props.
		 *	
		 * @return void
		 */
		private function setupInteractions():void {
			
			var countSegments:Number = _segments;
			
			//Create the line's segments.
	        for ( var segment:Object = _startPoint; countSegments > 0; countSegments-- ){
				
				segment = addPoint(
				
					segment,
					( _endPoint.x - _startPoint.x ) / _segments,
					( _endPoint.y - _startPoint.y ) / _segments
					
				);
				
			}
			
			_endPoint = { x:_endPoint.x, y:_endPoint.y, previousPoint:segment };
			segment.nextPoint = _endPoint;
			
			activeListeners();
			
		}
		
		/**
		 *  Update the line.
		 *	Re-draw the line's segments.
		 *	
		 *  @return:void
		 */
		private function update( event:Event = null ):void {
			
			//Clear the old line.
			graphics.clear();
			
			//Setup linestyle and move the pointer.
			graphics.lineStyle(	lineHeight, lineColor );
			graphics.moveTo( _startPoint.x, _startPoint.y );
			
			//Alter segments coordinates.
			for( var segment = _startPoint.nextPoint; segment.nextPoint; segment = segment.nextPoint ){

				//Calc line's segment length
				var xSegmentLength = ( segment.previousPoint.x + segment.nextPoint.x ) / 2;
				var ySegmentLength = ( segment.previousPoint.y + segment.nextPoint.y ) / 2;
				
				//Calc line's segment final point.
				segment.xStep = segment.xStep + ( xSegmentLength - segment.x ) * elasticity;
				segment.yStep = segment.yStep + ( ( ySegmentLength - segment.y ) * elasticity + ( gravity / _segments ) );
				
				//Apply Friction
				segment.xStep *= friction;
				segment.yStep *= friction;
				
				//Apply Gravity
				segment.x += segment.xStep;
				segment.y += segment.yStep;
				
			}
			
			//Draw the line
			for( var point:Object = _startPoint.nextPoint; point.nextPoint; point = point.nextPoint ){
				
				graphics.curveTo( point.x, point.y, ( point.nextPoint.x + point.x ) / 2, ( point.nextPoint.y + point.y ) / 2 );
				
			}
			
			//Finish the line.
			graphics.lineTo( point.x, point.y );
			
		}
		
		/**
		 *  Add Point.
		 *	Add a new point ( vertex ) at the line.
		 *	
		 *  @return:Object
		 */
	    private function addPoint( previousPoint, xStep, yStep ):Object {
			
			var segment:Object = {
				
				yStep:0,
				xStep:0,
				previousPoint:previousPoint,
				y:previousPoint.y + yStep,
				x:previousPoint.x + xStep
				
			};
			
			previousPoint.nextPoint = segment;
			
			return ( segment );
			
	    }
		
		/**
		 *	Active Listeners
		 *	Active/Desactive Listeners, based in Boolean parameter.
		 *	@param 	:Boolean
		 * 	@return	:void
		 */
		private function activeListeners( booVar:Boolean = true ):void {
			
			var method:String = booVar ? 'addEventListener' : 'removeEventListener';
			
			this[method]( Event.ENTER_FRAME, update );
			
		}
		
		/**
		 *  Dispose.
		 *	Turn off all listeners, remove childs and reset variables.
		 *	
		 *  @return:void
		 */
		private function dispose():void {
			
			graphics.clear();
			activeListeners( false );
			
		}
		
		 /*------------------------------------------------------------------------------------------------*\
		|* Class Handlers ---------------------------------------------------------------------------------*|
		\*------------------------------------------------------------------------------------------------*/
		
		/**
		 *  On Add to Stage.
		 *	Wait a AddToStage Event, to call instantiate method and init class.
		 *	@param 	event:Event;
		 *  @return	void
		 */
		private function addToStageHandler( event:Event ):void {
			
			//Remove Initial Listener
			removeEventListener( Event.ADDED_TO_STAGE, addToStageHandler );
			
			//Call instantiate method to init class.
			instantiate();
			
		}
		
		 /*------------------------------------------------------------------------------------------------*\
		|* Getters and Setters ----------------------------------------------------------------------------*|
		\*------------------------------------------------------------------------------------------------*/
		
		/**
		 *  Start point setter.
		 *	Receive one object with X and Y properties and add it in startPoint segment controller.
		 *	@param 	value:Object;
		 *  @return	void
		 */
		public function set startPoint( value:Object ):void {
			
			_startPoint.x = value.x;
			_startPoint.y = value.y;
			
		}
		
		/**
		 *  End point setter.
		 *	Receive one object with X and Y properties and add it in endPoint segment controller.
		 *	@param 	value:Object;
		 *  @return	void
		 */
		public function set endPoint( value:Object ):void {
			
			_endPoint.x = value.x;
			_endPoint.y = value.y;
			
		}
		
	}

}